/*
 * EnumFormatEtc.cpp
 *
 *  Created on: 2012-9-1
 *      Author: yangentao@gmail.com
 */

#include <windows.h>
#include <ole2.h>
#include <Unknwn.h>
#include <assert.h>
#include "EnumFormatEtc.h"

HRESULT CreateEnumFormatEtc(UINT nNumFormats, FORMATETC *pFormatEtc, IEnumFORMATETC **ppEnumFormatEtc) {
	if (nNumFormats == 0 || pFormatEtc == 0 || ppEnumFormatEtc == 0) return E_INVALIDARG ;

	*ppEnumFormatEtc = new EnumFormatEtc(pFormatEtc, nNumFormats);

	return (*ppEnumFormatEtc) ? S_OK : S_FALSE ;
}
static void DeepCopyFormatEtc(FORMATETC *dest, FORMATETC *source) {
	// copy the source FORMATETC into dest
	*dest = *source;
	if (source->ptd) {
		// allocate memory for the DVTARGETDEVICE if necessary
		dest->ptd = (DVTARGETDEVICE*) CoTaskMemAlloc(sizeof(DVTARGETDEVICE));
		// copy the contents of the source DVTARGETDEVICE into dest->ptd
		*(dest->ptd) = *(source->ptd);
	}
}

EnumFormatEtc::~EnumFormatEtc() {
	if (formats) {
		for (int i = 0; i < len; ++i) {
			if (formats[i].ptd) {
				CoTaskMemFree(formats[i].ptd);
			}
		}
		delete[] formats;
	}
}

EnumFormatEtc::EnumFormatEtc(FORMATETC formats_[], int length) {
	refCount = 1;
	pos = 0;
	len = length;
	formats = new FORMATETC[length];
	for (int i = 0; i < len; i++) {
		DeepCopyFormatEtc(&formats[i], &formats_[i]);
	}
}
ULONG __stdcall EnumFormatEtc::AddRef(void) {
	return InterlockedIncrement(&refCount);
}

ULONG __stdcall EnumFormatEtc::Release(void) {
	LONG r = InterlockedDecrement(&refCount);
	if (r == 0) {
		delete this;
		return 0;
	} else {
		return r;
	}
}
HRESULT __stdcall EnumFormatEtc::QueryInterface(REFIID riid, void** ppv) {
	*ppv = NULL;
	if (riid == IID_IUnknown || riid == IID_IEnumFORMATETC) {
		*ppv = this;
		AddRef();
		return S_OK;
	} else {
		*ppv = 0;
		return E_NOINTERFACE;
	}
}
HRESULT __stdcall EnumFormatEtc::Next(ULONG celt, FORMATETC *pFormatEtc, ULONG *pceltFetched) {
	if (celt==0 || pFormatEtc == NULL) {
		return E_INVALIDARG ;
	}
	// We only support one format, so this is simple.
	unsigned int copied = 0;
	while ((pos < len) && (copied < celt)) {
		DeepCopyFormatEtc(&pFormatEtc[copied], &formats[pos]);
		pos++;
		copied++;
	}
	if (pceltFetched) {
		*pceltFetched = copied;
	}
	return copied==celt ? S_OK : S_FALSE ;
}

HRESULT __stdcall EnumFormatEtc::Skip(ULONG celt) {
	pos += celt;
	return pos <= len ? S_OK : S_FALSE;
}

HRESULT __stdcall EnumFormatEtc::Reset(void) {
	pos = 0;
	return S_OK ;
}

HRESULT __stdcall EnumFormatEtc::Clone(IEnumFORMATETC **ppenum) {
	HRESULT hr = CreateEnumFormatEtc(len, formats, ppenum);
	if(hr == S_OK){
		((EnumFormatEtc*)(*ppenum))->pos = pos;
	}
	return hr;
}
